import re

from django_redis import get_redis_connection
from redis import StrictRedis
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.serializers import ModelSerializer
from rest_framework_jwt.settings import api_settings

from celery_tasks.email.tasks import send_active_email
from users.models import User


class CreateUserSerializer(ModelSerializer):
    """
    创建用户使用的序列化器，
    要校验的字段
    要序列化器定义的字段
    重写create，实现新增一个用户（注册）
    """

    # write_only表示只能写，不能读。例如定义序列化器时，password字段（还有短信验证码等）
    # 通常指定write_only=True。在序列化时，即对象转为字典、JSON字符串时，字典、JSON字符串不会
    # 包含这字段。但是反序列化时，即JSON字符串、字典转换为对象时，在参数校验的时候，校验通过，而且
    # 校验后的数据有password这个字段，并且能存进数据库。

    # 密码不想传到前端就加进来并设为只写
    #  我们数据表里面没有的字段,所以要定义字段为了反序列的校验
    password = serializers.CharField(label='密码',write_only=True)
    password2 = serializers.CharField(label='确认密码',write_only=True)
    sms_code = serializers.CharField(label='短信验证码',max_length=6,write_only=True)
    allow = serializers.BooleanField(label='同意用户协议',default=False,write_only=True)
    # 注册成功之后，自动登陆，需要生成并返回ＪＷＴ
    # jwd不要要校验　　设为只读　read_only
    token = serializers.CharField(label='JWT',read_only= True)


    def create(self,validated_data):
        # 创建一个用户对象
        # 不能时使用默认实现
        # return super().create(validated_data)
        # User.objects.create()  #密码不会加密

        # {'format': None,
        #  'view': < meiduo_mall.apps.users.views.CreateUserViewobjectat0x7fb6c0222828 >,
        # 'request': < rest_framework.request.Requestobjectat0x7fb6c0223080 >}
        # 序列化器的属性context: 是个字典。 包含了三个数据：request对象，视图，数据格式json或format表单
        # 我们可以通过这个属性获取到视图，数据格式，请求对象的数据；要获取当前登陆用户可以用request.user
        # print(self.context)
        print(self.context.get('format'))
        print(self.context.get('request').user)
        print(self.context.get('view'))

        # 因为序列化validated里面包含着所有的字段，而我们不需要把字段全返回 所以只要三个
        user = User.objects.create_user(    # 密码会加密
            username=validated_data.get('username'),
            password=validated_data.get('password'),
            mobile=validated_data.get('mobile')
            )
        # TODO: 生成ＪＷＴ
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER  # 生payload部分的方法
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER  # 生成jwt的方法

        #  {'exp': xxx, 'email': '', 'user_id': 1, 'username': 'admin'}
        # user：登录的用户对象
        payload = jwt_payload_handler(user)  # 生成payload, 得到字典
        token = jwt_encode_handler(payload)  # 生成jwt字符串
        user.token = token;

        return user

    def validate_mobile(self, value):
        """验证手机号"""
        if not re.match(r'^1[3-9]\d{9}$', value):
            raise serializers.ValidationError('手机号格式错误')
            # return Response({'message':'xxxxxx'}, status=400)
        return value

    def validate_allow(self, value):
        """检验用户是否同意协议"""
        if not value:
            raise serializers.ValidationError('请同意用户协议')
        return value

    def validate(self, attrs):
        # 判断两次密码
        if attrs['password'] != attrs['password2']:
            raise serializers.ValidationError('两次密码不一致')
            # 只能在视图里返回这个response  在序列器校验里返回只能返回到validate里面是不支持的
            # return Response({'message':'两次密码不一致'},status=404)

        # 高亮注释 todo:判断短信验证码
        # 从请求过来的数据里获取电话号
        mobile = attrs.get('mobile')
        # 获取正确的短信验证码
        strict_redis = get_redis_connection('sms_codes')  # type: StrictRedis
        real_sms_code = strict_redis.get('sms_%s' % mobile)
        if real_sms_code is None:
            raise ValidationError('无效的短信验证码')
        #获取用户传递来的验证码
        sms_code = attrs.get('sms_code')
        if sms_code != real_sms_code.decode():
            raise ValidationError('短信验证码错误')

        return attrs


    class Meta:
        model = User
        # 要序列化返回的字段　　因为password sms_code allow等都是只写属于不用返回的字段
        fields = ('id','username','mobile',  # <=所以我们返回只这些字段加上ＪＷＴ
                  'password','password2','sms_code','allow','token')

        extra_kwargs = {
            'username': {
                'min_length': 5,
                'max_length': 20,
                'error_messages': {
                    'min_length': '仅允许5-20个字符的用户名',
                    'max_length': '仅允许5-20个字符的用户名',
                }
            },
        }


class UserDetailSerializer(serializers.ModelSerializer):
    """ 用户详细信息序列化器 """
    class Meta:
        model = User
        fields = ('id', 'username', 'mobile', 'email', 'email_active')


class EmailSerializer(ModelSerializer):
    """修改用户: 邮箱"""

    def update(self, instance, validated_data):
        # 1. 校验邮箱
        # 2. 修改邮箱

        # 修改一条用户数据
        # instance: 一个用户对象
        instance.email = validated_data['email']
        instance.save()

        # 3. todo: 发送激活链接到用户的邮箱
        active_url = instance.generate_active_email()
        # 使用celery发送邮件
        # 参数1: 接收者
        # 参数2: 激活链接
        send_active_email.delay(instance.email, active_url)

        return instance

    class Meta:
        model = User
        fields = ('id', 'email')
        extra_kwargs = {
            'email': {  # EmailField
                'required': True
            }
        }

